package org.jeecg.common;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.util.RedisUtil;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.Callable;
import java.util.function.Predicate;

/**
 * 注释
 *
 * @author qiang.zhou
 * @createtime 2023-03-09 15:34
 */
@Slf4j
@Component
public class RedisCacheUtil {
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private ObjectMapper objectMapper;

    public String getStr(String key) {
        Object obj = redisUtil.get(key);
        return obj == null ? null : String.valueOf(obj);
    }

    public RedisUtil redisUtil() {
        return this.redisUtil;
    }

    /**
     * json字串反序列化后返回
     * @param clazz 反序列化的目标对象
     * @return
     */
    public <T> T getObj(String key, Long expire,  Callable<T> valueLoader, Class<T> clazz) {
        return this.getObj(key, expire, ObjectUtils::isNotEmpty, valueLoader, clazz);
    }

    /**
     * json字串反序列化后返回
     * @param typeReference 反序列化的目标对象
     * @return
     */
    public <T> T getObj(String key, Long expire,  Callable<T> valueLoader, TypeReference<T> typeReference) {
        return this.getObj(key, expire, ObjectUtils::isNotEmpty, valueLoader, typeReference);
    }

    /**
     * json字串反序列化后返回
     * @param clazz 反序列化的目标对象
     * @return
     */
    public <T> T getObj(String key, Long expire, Predicate<T> predicate, Callable<T> valueLoader, Class<T> clazz) {
        String json = getString(key, expire, predicate, valueLoader);
        return this.parseObject(json, clazz);
    }

    /**
     * json字串反序列化后返回
     * @author: qiang.zhou
     * @date: 2023/3/9 15:59
     * @param typeReference 反序列化的目标对象
     * @return T
     **/
    public <T> T getObj(String key, Long expire, Predicate<T> predicate, Callable<T> valueLoader, TypeReference<T> typeReference) {
        String json = getString(key, expire, predicate, valueLoader);
        return this.parseObject(json, typeReference);
    }

    /**
     *  使用Redis保存或者加载缓存的数据, 这个方法不可使用SpringBoot提供的CacheMange注解来管理缓存
     * @param key redis key
     * @param expire redis过期时间
     * @param predicate 保存至缓存的规则
     * @param valueLoader 数据加载
     * @return 直接返回json数据
     */
    public <T> String getString(String key, Long expire, Predicate<T> predicate, Callable<T> valueLoader) {
        if (StringUtils.isBlank(key)) {
            return null;
        }
        try{
            // 检查缓存中是否用当前key
            String json = this.getStr(key);
            if(StringUtils.isBlank(json)) {
                T call = valueLoader.call();
                if(predicate.test(call)) {
                    redisUtil.set(key, this.toJsonString(call), expire);
                }
                return this.toJsonString(call);
            }
            return json;
        } catch (Exception e) {
            log.error("处理函数失败： {}", e);
            throw new IllegalArgumentException(e);
        }
    }

    public String toJsonString(Object obj) {
        if(null == obj) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException(e);
        }
    }

    public <T> T parseObject(String json, Class<T> clazz) {
        if(!StringUtils.isNotBlank(json)) {
            return null;
        }
        try {
            return objectMapper.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            log.error(e.getMessage());
            throw new IllegalArgumentException(e);
        }
    }

    public <T> T parseObject(String json, TypeReference<T> clazz) {
        try {
            return objectMapper.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            log.error(e.getLocalizedMessage());
            throw new IllegalArgumentException(e);
        }
    }

    public void hdel(String hkey, String item) {
        redisUtil.hdel(hkey, item);
    }

    public void hset(String hkey, String item, Object object) {
        redisUtil.hset(hkey, item, toJsonString(object));
    }

    public <T> T hget(String hkey, String item, Class<T> clazz) {
        Object obj = redisUtil.hget(hkey, item);
        if (null == obj) {
            return null;
        }
        if (obj instanceof String) {
            return parseObject((String) obj, clazz);
        }
        return parseObject(this.toJsonString(obj), clazz);
    }

}
